"""
Python元组(tuple)教学

元组是Python中的不可变序列类型，一旦创建就不能修改。
元组和列表非常相似，但元组使用圆括号()，而列表使用方括号[]。列表可以修改，而元组不能。
"""

# =============== 1. 元组的创建 ===============

print("="*50)
print("1. 元组的创建")
print("="*50)

# 创建空元组
empty_tuple = ()
print(f"空元组: {empty_tuple}")

# 创建包含不同类型元素的元组
mixed_tuple = (1, "Hello", 3.14, True)
print(f"混合类型元组: {mixed_tuple}")

# 创建单元素元组 - 注意：必须在元素后加逗号，否则会被当作普通数据
# 错误方式（这不是元组，而是一个整数）
not_a_tuple = (5)
print(f"这不是元组: {not_a_tuple}, 类型: {type(not_a_tuple)}")  # <class 'int'>

# 正确方式（这是一个只有一个元素的元组）
single_element_tuple = (5,)
print(f"单元素元组: {single_element_tuple}, 类型: {type(single_element_tuple)}")  # <class 'tuple'>

# 也可以省略圆括号，Python会自动识别为元组
# 多个值逗号分隔赋值到一个变量中，也会被识别为元组
# 同理，当一个函数有多个返回值时，实际上也是返回一个元组，如果我们用一个变量承接，那么这个变量就是元组
another_tuple = 1, 2, 3, 4, 5
print(f"无括号元组: {another_tuple}, 类型: {type(another_tuple)}")

# 使用tuple()函数将其他序列转换为元组
tuple_from_list = tuple([1, 2, 3])
print(f"从列表创建元组: {tuple_from_list}")

tuple_from_string = tuple("Python")
print(f"从字符串创建元组: {tuple_from_string}")  # 每个字符成为一个元素


# =============== 2. 元组的成员访问 ===============

print("\n"+"="*50)
print("2. 元组的成员访问")
print("="*50)

# 通过索引访问元组元素（索引从0开始）
coordinates = (10.5, 20.8)
print(f"坐标: {coordinates}")
print(f"X坐标: {coordinates[0]}")
print(f"Y坐标: {coordinates[1]}")

# 使用负索引从后向前访问（-1表示最后一个元素）
weekdays = ("周一", "周二", "周三", "周四", "周五", "周六", "周日")
print(f"一周的天数: {weekdays}")
print(f"一周的第一天: {weekdays[0]}")
print(f"一周的最后一天: {weekdays[-1]}")
print(f"一周的倒数第二天: {weekdays[-2]}")

# 尝试修改元组会引发错误
# try...except... 是异常处理语句，用于捕获和处理异常
try:
    weekdays[0] = "星期一"  # 这会引发错误
except TypeError as e:
    print(f"错误信息: {e}")  # 'tuple' object does not support item assignment


# =============== 3. 元组的切片 ===============

print("\n"+"="*50)
print("3. 元组的切片")
print("="*50)

numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
print(f"完整元组: {numbers}")

# 从索引2到索引5（不包括6）
subset = numbers[2:6]
print(f"切片 [2:6]: {subset}")

# 从开始到索引4（不包括5）
start_to_4 = numbers[:5]
print(f"切片 [:5]: {start_to_4}")

# 从索引6到结束
from_6_to_end = numbers[6:]
print(f"切片 [6:]: {from_6_to_end}")

# 使用步长（每隔2个元素取一个）
step_2 = numbers[::2]
print(f"步长为2的切片 [::2]: {step_2}")

# 反向切片（从后向前）
reverse = numbers[::-1]
print(f"反向切片 [::-1]: {reverse}")


# =============== 4. 获取元组长度 ===============

print("\n"+"="*50)
print("4. 获取元组长度")
print("="*50)

colors = ("红", "橙", "黄", "绿", "青", "蓝", "紫")
print(f"颜色元组: {colors}")
print(f"颜色数量: {len(colors)}")

# 空元组长度为0
print(f"空元组长度: {len(())}")


# =============== 5. 元素存在性检查 ===============

print("\n"+"="*50)
print("5. 元素存在性检查 (in 和 not in)")
print("="*50)

fruits = ("苹果", "香蕉", "橙子", "葡萄", "西瓜")
print(f"水果元组: {fruits}")

# 检查元素是否存在
print(f"'香蕉' 在水果元组中: {'香蕉' in fruits}")
print(f"'梨' 在水果元组中: {'梨' in fruits}")

# 检查元素是否不存在
print(f"'梨' 不在水果元组中: {'梨' not in fruits}")
print(f"'香蕉' 不在水果元组中: {'香蕉' not in fruits}")

# 使用条件语句
if "苹果" in fruits:
    print("找到了苹果！")
else:
    print("没有找到苹果。")


# =============== 6. 元组解包 ===============

print("\n"+"="*50)
print("6. 元组解包")
print("="*50)

# 基本解包 - 元素数量必须匹配
person = ("张三", 30, "北京")
name, age, city = person
print(f"姓名: {name}, 年龄: {age}, 城市: {city}")

# 使用*语法收集多余的元素
numbers = (1, 2, 3, 4, 5, 6)
first, second, *rest = numbers
print(f"第一个: {first}, 第二个: {second}, 其余: {rest}")

# *可以在任何位置使用
*beginning, last = numbers
print(f"开始的元素: {beginning}, 最后一个: {last}")

first, *middle, last = numbers
print(f"第一个: {first}, 中间的: {middle}, 最后一个: {last}")

# 交换变量值（利用元组解包）
a, b = 5, 10
print(f"交换前: a = {a}, b = {b}")
a, b = b, a  # 使用元组解包交换值
print(f"交换后: a = {a}, b = {b}")
# 过去我们交换两个变量的值，需要借助第三个变量，现在也可以直接交换

# 函数返回多个值时，实际上是返回一个元组
def get_user_info():
    """返回用户信息（姓名，年龄，邮箱）"""
    return "李四", 25, "lisi@example.com"

# 解包函数返回值
user_name, user_age, user_email = get_user_info()
print(f"用户姓名: {user_name}, 年龄: {user_age}, 邮箱: {user_email}")


# =============== 7. 实际应用例子 ===============

print("\n"+"="*50)
print("7. 元组的实际应用")
print("="*50)

# 例子：使用元组存储API密钥（不可变，避免意外修改）
API_KEYS = (
    "sk-abcdef123456",  # OpenAI
    "sk-ghijkl789012",  # DeepSeek
    "sk-mnopqr345678"   # Moonshot
)

print("可用的API密钥:")
for i, key in enumerate(API_KEYS):
    # 在实际代码中不要这样打印密钥，这里仅作示例
    print(f"  服务 {i+1}: {key[:5]}...")  # 只显示前5个字符，后面用...代替

# 尝试修改密钥会导致错误
try:
    API_KEYS[0] = "new_key"  # 这会引发错误
except TypeError as e:
    print(f"修改API密钥时出错: {e}")

print("\n元组学习结束！元组是一种简单却强大的数据结构，尤其适合表示那些一旦创建就不应该更改的数据。")
